Browse Source

initial commit of ao-mud

main
deicidus 2 years ago
commit
ccf33b459c
  1. 52
      README.md
  2. 20
      spells/ask
  3. 48
      spells/colors
  4. 29
      spells/copy
  5. 61
      spells/cursor
  6. 44
      spells/detect-distro
  7. 91
      spells/detect-magic
  8. 89
      spells/disenchant
  9. 45
      spells/enchant
  10. 12
      spells/forall
  11. 33
      spells/hash
  12. 80
      spells/jump-to-marker
  13. 27
      spells/mark-location
  14. 74
      spells/menu/await_keypress
  15. 27
      spells/menu/cursor-blink
  16. 18
      spells/menu/fathom-cursor
  17. 22
      spells/menu/fathom-terminal
  18. 27
      spells/menu/max-length
  19. 71
      spells/path-wizard
  20. 82
      spells/read-magic
  21. 40
      spells/rehashchant
  22. 11
      spells/say
  23. 48
      spells/spellbook
  24. 102
      spells/update-all
  25. 18
      spells/wizard_eyes

52
README.md

@ -0,0 +1,52 @@
# ao-mud
ao-mud is a project to make the terminal easier for novice users by narrativizing it as a Multi-User Dungeon, or M.U.D.
# Principles:
* story first - make the world make sense, and that will tell us what to code
* playtest-driven development - try to use it, fail fast and iterate; it has to be fun
* POSIX-compatible scripts - maximally compatible across platforms and weird nested environments
* immanence - the game is here, a meaningful virtual space in the terminal
* immediacy - the game is everywhere, not a separate mode
* pedagogy - collecting best practices and making them easy to use, learn, remember, and teach/share
* sharpen the saw - gradually improving the architecture of the tech stack
* smarter conventions - we can evolve the terminal and make it easier to teach by migrating to higher-order semantics (new words)
# Architecture
ao-mud is also ao-bash, an attempt to immanentize the AO to the terminal for a native un-AO experience that requires no database.
Some architectural guidelines:
* Make many small, atomic scripts that are almost as atomic as possible
* The smallest logical size is a semantic unit: (e.g., read-magic handles both reading all attributes or one named attribute, could abstract read-all-magic and read-all-magic but it's semantically parsimonious to combine them)
* Make each script work either sourced or executed by using the BASH_SOURCE idiom (will be included after conversion to POSIX)
# The Beginning of Your AO Adventure...
What is the Autonomous Organization, what does it mean? This mystery drives your quest.
We all sense something is wrong with the world. We all want to ask the same question.
The AO is the answer to this question.
Along the way, you will learn to use the Unix terminal, and you will gain special terminal upgrades. Most people don't believe that UI upgrades can be game-changing, but with cybernetics and a little magic, anything is possible.
# Three Paths of Knowledge
On your AO adventure, there are three different tutorials which gradually unlock the powers of the terminal. Choose your class wisely:
* Sorcerer: The Path of Sorcery is the path of casual power. Sorcerers learn how to wield powerful spells early on, but they don't know how they work. Sorcerers trade words and understanding and remember this knowledge in their memory.
* Wizard: The Path of Wizardry is a path of careful learning. Wizards start by learning the true names of bash commands, and learn to assemble spells gradually. Wizards trade spells on pages (bash scripts) and save them in their spellbook (spells folder).
* Alchemist: The Path of Art is an oral tradition. Find a bash alchemist who can teach you the elements and the ways of alchemy.
# Loading Saved Games
ao-mud contains unlockable menus and terminal shortcuts that make using the terminal easier and more fun. To skip ahead and turn some of these features on without unlocking them in tutorial mode, you can use a Save Code that you received from an earlier playthrough. To use a Save Code, open the AO main menu, select "Load Saved Game", enter your Save Code, and press Enter.
# How to Begin
To start your AO adventure, type ./ao in this directory.

20
spells/ask

@ -0,0 +1,20 @@
#!/usr/bin/env sh
# This magical incantation allows the terminal to ask a question and receive an answer.
ask() {
# Display the prompt
echo "${1}"
# Read the user's input
read -r user_input
# Return the user's input
echo "${user_input}"
}
# Check if script is being sourced or executed
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# Script is being executed, call the function and pass the arguments
ask "$@"
fi

48
spells/colors

@ -0,0 +1,48 @@
#!/usr/bin/env sh
# This magical knowledge serves as a colorful palette of hues and shades,
# granting control over the appearance of the terminal.
# Use these variables to add a splash of color to your scripts.
# Reset all attributes to their default values
RESET="\033[0m"
# Colors
BLACK="\033[30m"
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
BLUE="\033[34m"
MAGENTA="\033[35m"
CYAN="\033[36m"
WHITE="\033[37m"
# Bright colors
BRIGHT_BLACK="\033[30;1m"
BRIGHT_RED="\033[31;1m"
BRIGHT_GREEN="\033[32;1m"
BRIGHT_YELLOW="\033[33;1m"
BRIGHT_BLUE="\033[34;1m"
BRIGHT_MAGENTA="\033[35;1m"
BRIGHT_CYAN="\033[36;1m"
BRIGHT_WHITE="\033[37;1m"
# Background colors
BG_BLACK="\033[40m"
BG_RED="\033[41m"
BG_GREEN="\033[42m"
BG_YELLOW="\033[43m"
BG_BLUE="\033[44m"
BG_MAGENTA="\033[45m"
BG_CYAN="\033[46m"
BG_WHITE="\033[47m"
# Bright background colors
BG_BRIGHT_BLACK="\033[40;1m"
BG_BRIGHT_RED="\033[41;1m"
BG_BRIGHT_GREEN="\033[42;1m"
BG_BRIGHT_YELLOW="\033[103m"
BG_BRIGHT_BLUE="\033[104m"
BG_BRIGHT_MAGENTA="\033[105m"
BG_BRIGHT_CYAN="\033[106m"
BG_BRIGHT_WHITE="\033[107m"

29
spells/copy

@ -0,0 +1,29 @@
#!/bin/sh
# This spell copies the text file at the specified path to the clipboard.
# It takes one argument, the path to the text file.
if [ ! -f "$1" ]; then
echo "That file does not exist."
exit 1
fi
# Check if the `pbcopy` command is available (only on macOS). If it is, use it to copy the contents of the file to the clipboard.
if command -v pbcopy >/dev/null 2>&1; then
pbcopy < "$1"
# If `pbcopy` is not available, check if the `xsel` command is available (only on some Unix-based systems). If it is, use it to copy the contents of the file to the clipboard.
elif command -v xsel >/dev/null 2>&1; then
xsel --clipboard --input < "$1"
# If neither `pbcopy` nor `xsel` is available, check if the `xclip` command is available (only on some Unix-based systems). If it is, use it to copy the contents of the file to the clipboard.
elif command -v xclip >/dev/null 2>&1; then
xclip -selection clipboard < "$1"
# If none of the above commands are available, print an error message.
else
echo "Your spell fizzles. No clipboard utilities are available on this system. Please install xsel, xclip, or pbcopy (for mac)" >&2
exit 1
fi
echo "Copied $1 to your clipboard."

61
spells/cursor

@ -0,0 +1,61 @@
#!/usr/bin/env sh
# This magical knowledge of textual cantrips allows for powerful cursor prestidigitation,
# allowing you to control the position and appearance of the terminal cursor.
# Use these variables to move the cursor around the screen,
# hide and show it, and save and restore its position.
#
# Example usage:
# printf "$SAVE_CURSOR"
# printf "$MOVE_UP"
# printf "$MOVE_LEFT"
# printf "Hello, world!"
# printf "$RESTORE_CURSOR"
# Clearing
CLEAR_SCREEN="\033[2J"
CLEAR_LINE="\033[K"
# Save cursor position
SAVE_CURSOR="\033[s"
# Restore cursor position
RESTORE_CURSOR="\033[u"
# Move cursor (one line/column)
MOVE_UP="\033[A"
MOVE_DOWN="\033[B"
MOVE_RIGHT="\033[C"
MOVE_LEFT="\033[D"
# Move cursor to column n
# usage: move_to_column n
move_to_column() {
echo -en "\033[${1}G"
}
# Move cursor to row n
# usage: move_to_row n
move_to_row() {
echo -en "\033[${1}H"
}
# Move cursor to row n, column m
# usage: move_to_xy n m
move_to_xy() {
echo -en "\033[${1};${2}H"
}
# Hide and show cursor
HIDE_CURSOR="\033[?25l"
SHOW_CURSOR="\033[?25h"
# Scrolling
SCROLL_SCREEN="\033[S"
REVERSE_SCROLL_SCREEN="\033[T"
# Set scrolling region
# usage: set_scroll_region top bottom
set_scroll_region() {
echo -en "\033[${1};${2}r"
}

44
spells/detect-distro

@ -0,0 +1,44 @@
#!/usr/bin/env sh
# Detects what OS you are running and outputs a short string to identify that OS.
# Also saves the result in the $DISTRO environment variable.
# -v for more verbose output
source ./colors
source ./say
# Read arguments
while getopts v flag
do
case "${flag}" in
v) VERBOSE=true
;;
esac
done
# Output welcome message
if [ $VERBOSE ]; then say "Detecting which Linux distribution or other operating system you are using..."; fi
# Check for each known operating system one-by-one
if [ -f "/etc/debian_version" ]; then
DISTRO="debian"
if [ $VERBOSE ]; then say "${GREEN}Debian${RESET}, Ubuntu, or Raspbian OS detected."; fi
elif [ -f "/etc/arch-release" ]; then
DISTRO="arch"
if [ $VERBOSE ]; then say "${GREEN}Arch or Manjaro-based${RESET} OS detected."; fi
elif [ -f "/etc/fedora-release" ]; then
DISTRO="fedora"
if [ $VERBOSE ]; then say "${GREEN}Fedora${RESET} detected as the Operating System"; fi
elif [ $(uname | grep -c "Darwin") -eq 1 ]; then
DISTRO="mac"
if [ $VERBOSE ]; then say "${GREEN}MacOS${RESET} detected."; fi
else
# Failed to detect OS, so complain and exit
say unknown
exit 1
fi
# Output the short string for the detected OS
if [ ! $VERBOSE ]; then
say $DISTRO;
fi

91
spells/detect-magic

@ -0,0 +1,91 @@
#!/bin/sh
# This spell detects magic in the files in the current directory. It reveals any enchanted attributes hidden within the files and tells you how much magic it has detected.
# Define colors
blue="\033[36m"
purple='\033[0;35m'
reset='\033[0m'
# Define a function to print a message based on the total number of enchanted attributes
print_message() {
# Total number of enchanted attributes
count=$1
# High intensity message
if [ "$count" -gt 120 ]; then
# High intensity messages
messages=(
"Whoa, this room is seriously ${purple}enchanted${reset}!"
"I can feel the ${purple}magic${reset} emanating from these files!"
"This room is practically pulsating with ${purple}magic${reset}!"
"I've never sensed this much ${purple}magic${reset} in one place before!"
"The amount of ${purple}magic${reset} in this room is off the charts!"
"These files are practically glowing with ${purple}magic${reset}!"
"I can hardly contain all this ${purple}magic${reset}!"
)
# Select a random message
message=${messages[$RANDOM % ${#messages[@]} ]}
# Medium intensity message
elif [ "$count" -gt 50 ]; then
# Medium intensity messages
messages=(
"There seems to be quite a bit of ${purple}magic${reset} in this room."
"This room is positively brimming with ${purple}magic${reset}."
"I can feel the ${purple}magic${reset} in the air."
"These files are infused with ${purple}magic${reset}."
"The ${purple}magic${reset} in this room is palpable."
"The ${purple}magic${reset} in these files is almost tangible."
"This room is filled with ${purple}magic${reset}."
)
# Select a random message
message=${messages[$RANDOM % ${#messages[@]} ]}
# Low intensity message
elif [ "$count" -gt 15 ]; then
# Low intensity messages
messages=(
"I sense a bit of ${purple}magic${reset} in this room."
"There seems to be a hint of ${purple}magic${reset} in these files."
"I can feel a faint aura of ${purple}magic${reset} in this room."
"These files have a touch of ${purple}magic${reset}."
"I detect a faint trace of ${purple}magic${reset} in this room."
"There is a subtle essence of ${purple}magic${reset} in these files."
"This room has a faint glimmer of ${purple}magic${reset}."
)
# Select a random message
message=${messages[$RANDOM % ${#messages[@]} ]}
fi
# Print the message
echo -en "$message\n"
}
# Print a header row
printf "${blue}File${reset}%-30s ${purple}Enchantments${reset}\n"
# Iterate over all the files in the current directory
for file in *; do
# Use the read-magic spell to get a list of enchanted attributes for the file
attrs=$(./read-magic "$file" 2> /dev/null)
# If the read-magic spell returns a non-empty list of attributes,
# print the file name and the number of attributes
if [ -n "$attrs" ]; then
# Count the number of lines in the attribute list
count=$(echo "$attrs" | wc -l)
# Print the file name and the number of attributes in purple
printf "%-40s ${purple}%d${reset}\n" "$file" "$count"
# Add the number of attributes to the total
total=$((total + count))
fi
done
# If the total number of attributes is greater than 0, print a message
if [ "$total" -gt 0 ]; then
print_message "$total"
fi
# Reset the text color to the default
echo -en "$reset"

89
spells/disenchant

@ -0,0 +1,89 @@
#!/bin/sh
# This "disenchant" script allows you to remove an extended attribute from a file.
# To use this script, pass the file and the key name as arguments. If no key name is specified, the script will provide
# a menu of the extended attribute keys on the file and allow you to select one with the arrow keys to delete.
# The menu is only displayed if there are at least two extended attributes (not counting the '# file:' line).
# If only one extended attribute is found, the script will delete that attribute without displaying the menu.
# Disenchant a single attribute from a file
disenchant_one() {
key="$1"
file="$2"
if command -v xattr > /dev/null 2>&1; then
# xattr is available, use it
xattr -d "$key" "$file"
elif command -v attr > /dev/null 2>&1; then
# attr is available, use it
attr -r "$key" "$file"
elif command -v setfattr > /dev/null 2>&1; then
# setfattr is available, use it
setfattr -x "$key" "$file"
else
# xattr, attr, and setfattr are not available
echo "Error: xattr, attr, and setfattr are not available on this system."
exit 1
fi
echo "Disenchanted $key attribute from $file."
}
# Check if a file was given as an argument
if [ -z "$1" ]; then
# If no file was given, print an error message and exit
echo "Error: No file specified. Usage: disenchant file [key]"
exit 1
fi
# Read the extended attribute keys from the file
keys=$(./read-magic "$1")
# Extract the attribute keys from the output of read-magic
keys=$(echo "$keys" | awk -F: '{print $1}' | tr -d ' ')
echo $keys
# Check if a key name was given as an argument
if [ -z "$2" ]; then
# If no key name was given, check if there are at least two extended attributes
if [ $(echo "$keys" | wc -l) -lt 2 ]; then
# If there is only one extended attribute, delete it
key=$(echo "$keys")
disenchant_one "$key" "$1"
else
# If there are at least two extended attributes, provide a menu of the keys and select one with the arrow keys
keys="$keys disenchant-all"
times=$(($(echo "$keys" | wc -l) + 1))
desc="# disenchant this"
descriptions=()
for ((i = 1; i <= $times; i++)); do
descriptions+=("$desc")
done
echo "${descriptions[@]}"
menu "Choose which attribute to disenchant:" "$keys" $descriptions "menu_choice" # todo / problem: $descriptions needs to be a string but then it's only space-delimited so many commands cant work. solution is to refactor the menu script, splitting it up into many small and complete semantic scripts, and building a better more holistic and composed menu with lots of better building blocks. or using bmenu.
echo $menu_choice
key=${keys[$menu_choice]}
if [ -n "$key" ]; then
if [ "$key" = "disenchant all" ]; then
# Disenchant all extended attributes
for key in $keys; do
disenchant_one "$key" "$1"
done
echo "Disenchanted all attributes from $1."
break
else
# Disenchant the selected attribute
echo KEY $key
echo FILE $1
disenchant_one "$key" "$1"
break
fi
else
# If no key was selected, print an error message and exit
echo "Error: No key selected."
exit 1
fi
fi
else
# If a key name was given, use it as the key
key="$2"
# Disenchant the selected attribute
disenchant_one "$key" "$1"
fi

45
spells/enchant

@ -0,0 +1,45 @@
#!/bin/sh
# This spell enchants the specified file with the given attribute and value.
# If the attribute already exists, it is overwritten.
# If the file or attribute does not exist, it raises an error.
# Check that the correct number of arguments was provided
if [ $# -lt 3 ] | [ $# -gt 4 ]; then
echo "Error: This spell requires three or four arguments: a file path, an attribute name, and a value."
exit 1
fi
# Set the file path, attribute name, and attribute value variables
file=$1
attribute=$2
value=$3
# Check that the file exists
if [ ! -f "$file" ]; then
echo "Error: The file does not exist."
exit 1
fi
# Define a function to set the attribute value using the available commands
set_attribute_value() {
# Try to set the attribute using the 'attr' command
attr -s "$1" -V "$2" "$3" 2>&1 >/dev/null ||
# If the 'attr' command is not available, try using the 'xattr' command
xattr -w "$1" "$2" "$3" 2>&1 > /dev/null ||
# If the 'xattr' command is not available, try using the 'setfattr' command
setfattr -n "$1" -v "$2" "$3" 2>&1 > /dev/null ||
# If none of the commands are available, raise an error
(
echo "Error: This spell requires the 'attr', 'xattr', or 'setfattr' command to be installed."
exit 1
)
}
# Set the attribute value using the set_attribute_value function
set_attribute_value "$attribute" "$value" "$file"
echo "The file has been enchanted with the attribute '$attribute' and value '$value'."

12
spells/forall

@ -0,0 +1,12 @@
#!/bin/sh
# This spell allows you to cast a magic spell on all the files in the current directory. Simply provide the spell as
# an argument, and it will be cast on each file in turn.
#
# Use this spell to perform powerful magic on all your files at once (or to simply perform a batch operation on
# multiple files).
for file in ./*; do
echo "${file#./}"
"$@" "$file" | awk '{print " " $0}'
done

33
spells/hash

@ -0,0 +1,33 @@
#!/bin/sh
# The Hash Spell
#
# This powerful spell allows you to compute the CRC-32 hash of a text file, including its filename. Simply provide
# the path to the file as an argument, and the spell will do the rest. The resulting hash will be displayed on the
# command line.
if [ "$#" != 1 ]; then
echo "Usage: hash file"
exit 1
fi
# Get the directory where the script is being called from
script_dir="$(cd "$(dirname "$0")" >/dev/null 2>&1 && pwd)"
# Get the file name specified as an argument
file_name="$1"
# Concatenate the script directory and the file name to get the full file path
file="$script_dir/$file_name"
if [ ! -f "$file" ]; then
echo "Your spell fizzles. There is no file."
exit 1
fi
echo $file
# Compute the CRC-32 hash
crc=$(cksum "$file" | awk '{print $1}')
# Convert the hash to hexadecimal and display it
printf "0x%x\n" "$crc"

80
spells/jump-to-marker

@ -0,0 +1,80 @@
#!/bin/sh
# This 'jump' spell teleports you to the location marked by the 'mark-location' spell.
# If no location has been marked, or if you are already at the marked location, the spell will fizzle.
# Because the script uses 'cd' to change your location, you must source it. This can be accomplished with "source ./jump-to-marker".
# Having to source the script explicitly can be avoided if the spell is 'memorized' (sourced in .bashrc).
# The spell will offer to do this, installing 'jump' as a systemwide keyword for you.
# Check if the "jump" spell is already installed
if ! grep -q "jump" ~/.bashrc; then
printf "You have not yet memorized the 'jump-to-marker' spell, so you can't cast it anywhere. This spell is so powerful that it must be memorized (sourced in .bashrc) to use it as a normal command. Memorize the 'jump' spell now? (y/n) "
read -r REPLY
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
# Install the "jump" spell in the user's bashrc file
SCRIPT_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")")
printf "source $SCRIPT_DIR/jump-to-marker\n" >> ~/.bashrc
printf "The 'jump' spell has been memorized. Use the 'spellbook' command to forget memorized spells. You must open a new terminal or 'source ~/.bashrc' before using 'jump'.\n"
fi
fi
# 'jump' spell function (will be loaded when this file is sourced by .bashrc)
jump() {
# Set the path to the marker file
marker_file="$HOME/.mud/portal_marker"
# Check if the marker file exists
if [ -e "$marker_file" ]; then
# If the marker file exists, read the destination path from the file
destination="$(cat "$marker_file")"
# Check if the current directory is already the destination
if [ "$(pwd)" == "$destination" ]; then
# If the current directory is already the destination, inform the user
echo "You are already at the marked location."
else
# If the current directory is not the destination, change the current directory to the destination
cd "$destination"
# Print a random teleportation message
messages=("You feel a sudden warmth as you are enveloped in a glowing aura. When it fades, you find yourself at your destination."
"A strange sensation comes over you as you close your eyes. When you open them again, you are standing at your destination."
"You focus your mind on the marked location and feel yourself being pulled through the fabric of space. You open your eyes to find yourself at your destination."
"You feel a jolt as you are momentarily suspended in midair. When you land, you find yourself at your destination."
"You close your eyes and take a deep breath. When you open them, you are standing at your destination."
"A swirling vortex appears before you. You take a step forward and find yourself at your destination."
"You feel a tug on your stomach as you are suddenly pulled through a tunnel of light. When you emerge, you are at your destination."
"You hold out your hand and a bright portal appears. You step through and find yourself at your destination."
"You feel a rush of wind as you are teleported to your destination."
"You hear a faint ringing in your ears as you are transported to your destination."
"You close your eyes and visualize your destination. When you open them, you are standing there."
"You step into a glowing portal and are immediately transported to your destination."
"You feel a tug on your ankle as you are pulled through a wormhole. When you emerge, you are at your destination."
"You hear a faint hum as you are enveloped in a field of energy. When it dissipates, you are at your destination."
"You feel a sudden pressure on your chest as you are teleported to your destination."
"You feel a surge of magical energy as you are transported through the veil of reality."
"A glowing portal opens before you, and you step through it to your destination."
"A shimmering aura envelops you, and when it fades, you find yourself at the marked location."
"You close your eyes and focus, and when you open them again, you are standing at the marked location."
"A burst of light and sound surrounds you, and when it subsides, you are at the marked location."
"You feel a tug on your soul, and when it releases, you are standing at the marked location."
"You focus your magical energy and teleport to the marked location."
"A warp in the fabric of reality opens up, and you step through it to the marked location."
"You feel a sudden jolt, and when you regain your bearings, you are at the marked location."
"You close your eyes and visualize the marked location, and when you open them, you are there."
"You call upon the power of the elements to transport you to the marked location."
"You channel the energy of the celestial bodies to jump to the marked location."
"You recite an ancient incantation, and a magical portal appears, taking you to the marked location.")
echo "${messages[$RANDOM % ${#messages[@]}]}"
fi
else
# If the marker file does not exist, inform the user that no location has been marked
echo "No location has been marked. Use the 'mark-location' spell to mark a location before using the 'jump' spell."
fi
}
# If this script is no being called from within another script, call the main function and forward it our args (if sourced, it loads the function without running it)
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
jump "$@"
fi

27
spells/mark-location

@ -0,0 +1,27 @@
#!/bin/sh
# This "mark location" spell allows you to mark the current directory or file as the destination for a portal.
# To create a portal, navigate to the destination folder or file and cast this spell, then navigate to the location where
# you want to create the portal and cast the "create portal" spell.
# If an argument is given, it will be used as the path to the destination file or directory.
# If no argument is given, the current directory will be used as the destination.
# Create ~/.mud direrctory if does not exist
mkdir -p ~/.mud
# Set the path to the marker file
marker_file="$HOME/.mud/portal_marker"
# Check if an argument was given
if [ -z "$1" ]; then
# If no argument was given, use the current directory as the destination
destination="$(pwd)"
else
# If an argument was given, use it as the destination
destination="$1"
fi
# Save the destination path to the marker file
echo "$destination" > "$marker_file"
echo "Location marked at $destination."

74
spells/menu/await_keypress

@ -0,0 +1,74 @@
#!/usr/bin/env sh
# This magical spell captures and processes key presses.
# Use it to capture key presses from the terminal and determine which key was pressed.
# todo: Tab key not distinguished from Enter (\t case does not work)
# Define the function to handle key presses
handle_key() {
# Read a single character from the terminal
read -s -n1 key
#echo $key > ./invisible_character # debug
# Check the value of the key
case "$key" in
# Enter key
$'\t')
echo "tab"
;;
$'\n')
echo "enter"
;;
'') # also fires for Tab key
echo "enter"
;;
'')
echo "backspace"
;;
$'\b')
echo "backspace"
;;
# Escape sequence, could be any number of keys depending on next character
$'\e')
read -rsn2 -t 0.1 key
case "$key" in
'')
echo 'escape'
;;
# Up arrow key
$'[A')
echo "up"
;;
# Down arrow key
$'[B')
echo "down"
;;
# Right arrow key
$'[C')
echo "right"
;;
# Left arrow key
$'[D')
echo "left"
;;
$'[3')
echo "delete"
;;
*)
echo "escaped key: $key"
;;
esac
;;
# Any other key
*)
echo "$key"
# od -c <<< $key # debug
;;
esac
}
# Check if the script is being executed or sourced, if executed then call the function
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
handle_key
fi

27
spells/menu/cursor-blink

@ -0,0 +1,27 @@
#!/usr/bin/env sh
# This spell awakens or stills the cyclic illumination of the cursor.
# Define the function to turn the cursor blink on
cursor_blink_on() {
# Turn the cursor blink on
printf "\033[?25h"
}
# Define the function to turn the cursor blink off
cursor_blink_off() {
# Turn the cursor blink off
printf "\033[?25l"
}
# Check if the script is being called from another script
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# If not, call the appropriate function based on the command line argument
if [ "$1" = "on" ]; then
cursor_blink_on
elif [ "$1" = "off" ]; then
cursor_blink_off
else
echo "Usage: cast_cursor_blink on|off"
fi
fi

18
spells/menu/fathom-cursor

@ -0,0 +1,18 @@
#!/usr/bin/env sh
# This spell reveals the x and y coordinates of the cursor in the terminal window.
# Define the function to calculate the position of the cursor
fathom_cursor() {
# Get the position of the cursor
position=$(IFS=';' read -sdR -p $'\E[6n' ROW COL; printf "%s;%s" "${ROW#*[}" "$COL")
# Output the position of the cursor
echo "Cursor position: $position"
}
# Check if the script is being called from another script
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# If not, call the function
fathom_cursor
fi

22
spells/menu/fathom-terminal

@ -0,0 +1,22 @@
#!/usr/bin/env sh
# This magical spell reveals the dimensions of the terminal window, displaying the width and height as if by magic.
# Define the function to calculate the dimensions of the terminal window
fathom_dimensions() {
# Get the width of the terminal window
width=$(tput cols)
# Get the height of the terminal window
height=$(tput lines)
# Output the dimensions of the terminal window
echo "Width: $width"
echo "Height: $height"
}
# Check if the script is being called from another script
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# If not, call the function
fathom_dimensions
fi

27
spells/menu/max-length

@ -0,0 +1,27 @@
#!/usr/bin/env sh
# This magical spell reveals the maximum length of a list of strings.
# Usage: cast_max_length string1 string2 ...
# Define the function to calculate the maximum length of the given strings
max_length() {
# Initialize the maximum length to 0
max_length=0
# Loop through the arguments and calculate the maximum length
for string in "$@"; do
length=${#string}
if [ $length -gt $max_length ]; then
max_length=$length
fi
done
# Output the maximum length
echo "Maximum length: $max_length"
}
# Check if the script is being called from another script
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# If not, call the function and pass it the command line arguments
max_length "$@"
fi

71
spells/path-wizard

@ -0,0 +1,71 @@
#!/bin/sh
# This spell allows you to add or remove directories from your PATH environment variable permanently.
# It uses the 'grep' and 'sed' commands to search and edit the '.bashrc' file in your home directory.
# Check if the correct number of arguments was provided
if [ "$#" -ne 1 ] && [ "$#" -ne 2 ]; then
echo "Error: This spell requires one or two arguments: 'add' or 'remove' and an optional directory path."
exit 1
fi
# Set the action and directory variables
action=$1
directory=$2
# Check if the action is 'add' or 'remove'
if [ "$action" != "add" ] && [ "$action" != "remove" ]; then
echo "Error: The first argument must be 'add' or 'remove'."
exit 1
fi
# Check if the directory path was provided
if [ -z "$directory" ]; then
# If no directory path was provided, use the current directory
directory=$(pwd)
else
# If a directory path was provided, expand the '~' character if necessary
directory="${directory/#\~/$HOME}"
fi
# Check if the directory exists
if [ ! -d "$directory" ]; then
echo "Error: The directory does not exist."
exit 1
fi
# Check if the '.bashrc' file exists in the home directory
if [ ! -f "$HOME/.bashrc" ]; then
echo "Error: The '.bashrc' file does not exist in your home directory."
exit 1
fi
# Check if the directory is already in the PATH variable
if grep -q "$directory" "$HOME/.bashrc"; then
# If the directory is already in the PATH variable, check if the action is 'add'
if [ "$action" = "add" ]; then
echo "The directory is already in your PATH."
exit 0
fi
# If the action is 'remove' and the directory is already in the PATH variable, remove the directory from the PATH variable using the 'sed' command
sed -i "\|$directory|d" "$HOME/.bashrc"
echo "The directory has been removed from your PATH."
else
# If the directory is not in the PATH variable, check if the action is 'add'
if [ "$action" = "add" ]; then
# If the action is 'add', append the directory to the PATH variable in the '.bashrc' file
echo "export PATH=$PATH:$directory" >> "$HOME/.bashrc"
echo "The directory has been added to your PATH."
else
# If the action is 'remove' and the directory is not in the PATH variable, display an error message
echo "Error: The directory is not in your PATH."
exit 1
fi
fi
if [ "$action" = "add" ]; then
echo "The changes to your PATH will not take effect until you open a new terminal or run the 'source ~/.bashrc' command in your current terminal."
else
echo "The changes to your PATH will not take effect until you open a new terminal."
fi

82
spells/read-magic

@ -0,0 +1,82 @@
#!/bin/sh
# This spell reads the enchanted attributes from the specified file.
# If a field name is provided as the second argument, it reads the attribute with that name.
# If the file or attribute does not exist, it raises an error.
# Check that the correct number of arguments was provided
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
echo "Error: This spell requires one or two arguments: a file path and an optional attribute name."
exit 1
fi
# Set the file path and attribute name variables
file=$1
attribute=$2
# Check that the file exists
if [ ! -f "$file" ]; then
echo "Error: The file does not exist."
exit 1
fi
# Define a function to read the attribute value using the available commands
read_attribute_value() {
# Try to read the attribute using the 'attr' command
attribute_value=$(attr -g "$1" "$2" 2> /dev/null | cut -d ":" -f 2 | xargs)
# If the 'attr' command is not available, try using the 'xattr' command
if [ -z "$attribute_value" ]; then
attribute_value=$(xattr -p "$1" "$2" 2> /dev/null | cut -d ":" -f 2 | xargs)
fi
# If the 'xattr' command is not available, try using the 'getfattr' command
if [ -z "$attribute_value" ]; then
attribute_value=$(getfattr -n "$1" --only-values "$2" 2> /dev/null)
fi
# Return the attribute value
echo "$attribute_value"
}
# If an attribute name was not provided, print all attributes in YAML format
if [ -z "$attribute" ]; then
attributes_found=0
# Define a function to process each attribute
process_attribute() {
# Read the attribute value using the read_attribute_value function
value=$(read_attribute_value "$1" "$file")
# Check if the attribute value was set
if [ -n "$value" ]; then
# Print the attribute and value in YAML format
printf "%s: %s\n" "$1" "$value"
attributes_found=$((attributes_found + 1))
fi
}
# Call the process_attribute function for each attribute
for attribute in $(getfattr -m ".*" -e hex "$file" | cut -d ":" -f 1 | cut -d "." -f 2 | tr -d ' '); do
process_attribute "$attribute"
done
# Check if no extended attributes were found
if [ "$attributes_found" -eq 0 ]; then
echo "No enchanted attributes found."
fi
# If an attribute name was provided, print the attribute value
else
# Read the attribute value using the read_attribute_value function
value=$(read_attribute_value "$attribute" "$file")
# Check if the attribute value was set
if [ -z "$value" ]; then
echo "Error: The attribute does not exist."
exit 1
fi
# Print the attribute value
echo "$value"
fi

40
spells/rehashchant

@ -0,0 +1,40 @@
#!/bin/sh
# This "enchant" spell imbues a file with the power of its own hash, binding it to its own unique identity.
# The spell requires a single argument: the path to the file to be enchanted.
# The resulting CRC-32 hash is saved as an extended attribute called "user.hash" in hexadecimal.
# Check if the required argument was provided
if [ -z "$1" ]; then
# If no argument was given, display an error message and exit
echo "Error: No file specified."
exit 1
fi
# Set the path to the file
file="$1"
# Check if the file exists
if [ ! -e "$file" ]; then
# If the file does not exist, display an error message and exit
echo "Error: File not found."
exit 1
fi
# Calculate the hash of the file
hash=$(echo -n "$file" | cksum | awk '{ print "0x"$1 }')
# Save the hash as an extended attribute
if command -v attr > /dev/null; then
attr -s "hash" -V "$hash" "$file" 2>&1 >/dev/null
elif command -v xattr > /dev/null; then
xattr -w "user.hash" "$hash" "$file" 2>&1 >/dev/null
elif command -v setfattr > /dev/null; then
setfattr -n "user.hash" -v "$hash" "$file" 2>&1 > /dev/null
else
# If neither xattr or attr is available, display an error message and exit
echo "Error: xattr and attr commands not found. Cannot enchant file."
exit 1
fi
echo "File enchanted with hash: $hash"

11
spells/say

@ -0,0 +1,11 @@
#!/usr/bin/env sh
# 'say' is a more legible synonym for echo/printf
# Help! Why is this printing a newline??? printf isn't supposed to. Update: It is printing no newline at first then it starts printing one. ???
say() {
printf "%b\n" "${1}"
}
say_inline() {
printf "%b" "${1}"
}

48
spells/spellbook

@ -0,0 +1,48 @@
#!/bin/sh
# 'Spellbook' spell
# This 'spellbook' spell allows you to view and manage your collection of memorized spells.
# To use this spell, simply cast it from any location. You will be presented with a list of spells that are currently memorized in your bashrc file, and a menu of options.
# 'Spellbook' spell function
spellbook() {
# Print a list of memorized spells
echo "Your current memorized spells are:"
spells=$(grep -o "source .*" ~/.bashrc | cut -d " " -f 2)
if [ -z "$spells" ]; then
echo "You have no memorized spells."
else
grep -o "source .*" ~/.bashrc | cut -d " " -f 2 | nl
fi
# Print a menu of options
echo "What do you want to do? "
options="Forget Exit"
select opt in $options; do
case "$opt" in
"Forget")
# Print a menu of spells to forget
echo "Which spell do you want to forget?"
select spell in $spells; do
# Remove the selected spell from the bashrc file
sed -i "\\|$spell|d" ~/.bashrc
break
done
break
;;
"Exit")
# Exit the 'spellbook' spell
break
;;
*)
# Print an error message if the input is invalid
echo "Invalid option. Please try again."
;;
esac
done
}
if [ "$0" = "${BASH_SOURCE[0]}" ]; then
spellbook "$@"
fi

102
spells/update-all

@ -0,0 +1,102 @@
#!/usr/bin/env sh
# Updates all system software.
# Requires that your OS is supported and detected by the detect-distro spell.
# On Arch-based distros, also offers to install pamac to automate rebuilding AUR packages with new changes to pull.
# -v for more verbose output
# Requires say, ask_y, and wizard_eyes, also using colors, all currently located here:
source ./colors
source ./ask_Yn
source ./say
source ./wizard_eyes
# Read arguments
while getopts v flag
do
case "${flag}" in
v) VERBOSE=true;;
esac
done
# Helper function for Arch to check if pamac is already installed
check_pamac() {
wizard_eyes "pacman -Qi pamac # check if pamac is installed"
if pacman -Qi pamac &>/dev/null; then
PAMAC_INSTALLED=1
else
PAMAC_INSTALLED=0
fi
}
# Helper function for Arch to install pamac
install_pamac() {
wizard_eyes "sudo pacman -S pamac"
sudo pacman -S pamac --noconfirm
}
# Catch Ctrl-C and exit immediately it's pressed (otherwise it just kills current operation)
# Help! This doesn't always work, or works with a delay for sudo prompts.
quit() {
exit 1
}
trap quit INT
wizard_eyes "You're a wizard, so executed terminal commands and other tips will display like this."
# Detect what OS we are using using the detect-distro spell, which puts the result in the $DISTRO environment variable
wizard_eyes "./detect-distro"
source ./detect-distro >/dev/null 2>&1
say "Updating system for ${GREEN}$DISTRO${RESET}."
if [ $VERBOSE ]; then say "You may be asked for your ${BLUE}sudo${RESET} password multiple times."; fi
# Based on what distro was detected, update the system in the correct way
case $DISTRO in
"debian")
wizard_eyes "sudo apt update"
sudo apt update
wizard_eyes "sudo apt autoremove"
sudo apt autoremove
wizard_eyes "sudo apt upgrade"
sudo apt upgrade
;;
"arch")
ask_Yn USE_PAMAC "Would you like to use ${BLUE}pamac${RESET} to automate rebuilding of updated AUR packages? (recommended)"
# If Yes, check for pamac install and offer to install it if it's not already installed
if [ "$USE_PAMAC" -eq 1 ]; then
check_pamac
if [ "$PAMAC_INSTALLED" -eq 0 ]; then
ask_Yn INSTALL_PAMAC "${BLUE}pamac${RESET} is not installed. Install it now?"
if [ "$INSTALL_PAMAC" -eq 1 ]; then
install_pamac
else
say "Ok, your AUR packages will not be automatically rebuilt, then."
fi
fi
fi
wizard_eyes "pacman -Syu # update software packages"
sudo pacman -Syu --noconfirm
# Use pamac to update repos
if [ "$USE_PAMAC" -eq 1 ] && [ "$PAMAC_INSTALLED" -eq 1 ]; then
say "Using pamac to rebuild AUR packages."
wizard_eyes "pamac update --aur --devel"
pamac update --aur --devel --no-confirm
fi
;;
"fedora")
wizard_eyes "sudo dnf update"
sudo dnf update
wizard_eyes "sudo dnf upgrade"
sudo dnf upgrade
;;
"mac")
install
wizard_eyes "sudo brew update"
sudo brew update
;;
esac

18
spells/wizard_eyes

@ -0,0 +1,18 @@
#!/usr/bin/env sh
# A special version of say that prints the text in grey text, indented.
# Use this to show commands before they are executed, to teach those on the path of wizardry.
source ./say
source ./ask_Yn
wizard_eyes() {
if [ -z $WIZARD ]; then check_wizard; fi
if [ -n "$WIZARD" ] && [ "$WIZARD" -eq 1 ]; then say " ${GREY}${1}${RESET}"; fi
}
# Todo next: Add remember/recall functions so that wizard status is remembered after first ask
check_wizard() {
ask_Yn WIZARD "First, let me ask, are you a wizard?"
export "WIZARD=$WIZARD"
}
Loading…
Cancel
Save