aboutsummaryrefslogtreecommitdiffstats
path: root/ves-init.sh
diff options
context:
space:
mode:
authorDouglas B. Rumbaugh <doug@douglasrumbaugh.com>2026-06-06 12:27:03 -0400
committerDouglas B. Rumbaugh <doug@douglasrumbaugh.com>2026-06-06 12:27:03 -0400
commit0ecfe53b2d271133fac36de11ecfc0f7e47840f0 (patch)
tree3ee8b5188936e350e15ff851b07a33031d366389 /ves-init.sh
parent04b385284a8559bde3df51bab950784a0fd28cfd (diff)
downloadsh-ves-0ecfe53b2d271133fac36de11ecfc0f7e47840f0.tar.gz
Initial version complete
I dusted this off after years and had Claude finish it for me. caveat emptor: this is largely (though not entirely) LLM generated as of this commit
Diffstat (limited to 'ves-init.sh')
-rwxr-xr-x[-rw-r--r--]ves-init.sh232
1 files changed, 221 insertions, 11 deletions
diff --git a/ves-init.sh b/ves-init.sh
index e323aaa..8885cf9 100644..100755
--- a/ves-init.sh
+++ b/ves-init.sh
@@ -7,8 +7,8 @@
#
# Note, the correct functioning of other sh-ves functions requires that this
# file be sourced prior to calling them. As such, they will check for
-# SH_VES_INIT to be created prior to doing anything. If is not suggested to try
-# to set this variable manually to get around sourcing this file.
+# SH_VES_INIT to be created prior to doing anything. It is not suggested to
+# try to set this variable manually to get around sourcing this file.
#
# This file also provides default values for several sh-ves configuration
# variables. You may override these settings by setting the variables
@@ -16,11 +16,13 @@
# or by overwriting their values after sourcing the file.
if [ -z "${XDG_DATA_HOME+x}" ]; then
- SHVES_ENV_DIR="$HOME/.local/share/ves/envs"
+ SHVES_DATA_DIR="$HOME/.local/share/ves"
else
- SHVES_ENV_DIR="$XDG_DATA_HOME/ves/envs"
+ SHVES_DATA_DIR="$XDG_DATA_HOME/ves"
fi
+SHVES_ENV_DIR="$SHVES_DATA_DIR/envs"
+
if [ -z "${SHVES_OVERRIDE_VARS+x}" ]; then
SHVES_OVERRIDE_VARS="PATH LDPATH LD_LIBRARY_PATH CPATH"
fi
@@ -31,20 +33,33 @@ else
SHVES_BIN="$SHVES_SCRIPTS_DIR"
fi
+if [ ! -d "$SHVES_ENV_DIR" ]; then
+ mkdir -p "$SHVES_ENV_DIR"
+fi
+
+#
+# Validation is done with case globs rather than grep so that it keeps
+# working even when an environment has truncated PATH.
+#
_shves_check_env_name() {
- if ! echo $1 | grep "^[[:alpha:][:digit:]_-]*$" > /dev/null; then
- printf "ERROR: Environment [%s] is invalid. Name must contain only letters, -, and _\n" $1 > /dev/stderr
- return 1
- fi
+ case $1 in
+ ""|*[!a-zA-Z0-9_-]*)
+ printf "ERROR: Environment [%s] is invalid. Name must contain only letters, digits, -, and _\n" "$1" >&2
+ return 1
+ ;;
+ esac
return 0
}
-
+#
+# Check that environment $1 exists. If a second argument is given, the
+# check is quiet; otherwise an error is printed on failure.
+#
_shves_check_env_exists() {
if [ ! -f "$SHVES_ENV_DIR/$1" ]; then
if [ "$#" -lt 2 ]; then
- printf "ERROR: Environment [%s] does not exist.\n" $1 > /dev/stderr
+ printf "ERROR: Environment [%s] does not exist.\n" "$1" >&2
fi
return 1
@@ -53,6 +68,196 @@ _shves_check_env_exists() {
return 0
}
+_shves_check_var_name() {
+ case $1 in
+ ""|[!a-zA-Z_]*|*[!a-zA-Z0-9_]*)
+ printf "ERROR: Variable name [%s] is invalid.\n" "$1" >&2
+ return 1
+ ;;
+ esac
+
+ return 0
+}
+
+#
+# Resolve and validate the target environment for a command. $1 is the
+# requested environment name (possibly empty); when empty, the active
+# environment is used. The name is checked for validity and existence,
+# and printed on success.
+#
+_shves_resolve_env() {
+ if [ -n "$1" ]; then
+ _shves_env="$1"
+ elif [ -n "$SHVES_ENV_NM" ]; then
+ _shves_env="$SHVES_ENV_NM"
+ else
+ printf "ERROR: No environment active or specified.\n" >&2
+ return 1
+ fi
+
+ if ! _shves_check_env_name "$_shves_env"; then
+ return 1
+ fi
+
+ if ! _shves_check_env_exists "$_shves_env"; then
+ return 1
+ fi
+
+ printf '%s' "$_shves_env"
+ return 0
+}
+
+#
+# Print the stored value of variable $2 within the environment file $1.
+# Prints nothing if the variable is not stored in the environment.
+#
+_shves_get_var() {
+ grep "^export_var:$2=" "$1" | cut -d '=' -f 2-
+}
+
+#
+# Returns 0 if variable $2 is stored in environment file $1, 1 otherwise.
+#
+_shves_has_var() {
+ grep "^export_var:$2=" "$1" > /dev/null
+}
+
+#
+# Set variable $2 to value $3 within environment file $1, replacing any
+# existing definition. The file is rewritten rather than edited in place
+# with sed, so values may safely contain characters special to sed.
+#
+_shves_set_var() {
+ grep -v "^export_var:$2=" "$1" > "$1.tmp"
+ printf "export_var:%s=%s\n" "$2" "$3" >> "$1.tmp"
+ mv "$1.tmp" "$1"
+}
+
+#
+# Remove variable $2 from environment file $1.
+#
+_shves_del_var() {
+ grep -v "^export_var:$2=" "$1" > "$1.tmp"
+ mv "$1.tmp" "$1"
+}
+
+#
+# Expand value $2 for path-like variable $1 according to the sh-ves
+# convenience rules: values not beginning with / or ./ are taken to be
+# relative to $SHVES_DATA_DIR/bin (for PATH) or $SHVES_DATA_DIR/lib
+# (for LDPATH and LD_LIBRARY_PATH). Other variables are left untouched.
+#
+_shves_expand_path() {
+ case $2 in
+ /*|./*)
+ printf '%s' "$2"
+ return 0
+ ;;
+ esac
+
+ case $1 in
+ PATH)
+ printf '%s' "$SHVES_DATA_DIR/bin/$2"
+ ;;
+ LDPATH|LD_LIBRARY_PATH)
+ printf '%s' "$SHVES_DATA_DIR/lib/$2"
+ ;;
+ *)
+ printf '%s' "$2"
+ ;;
+ esac
+}
+
+#
+# Returns 0 if variable $1 is recorded in the activation save-state. The
+# names in SHVES_SAVED_VARS are space-separated, so padding both the list
+# and the pattern with spaces matches whole names only.
+#
+_shves_is_saved() {
+ case " $SHVES_SAVED_VARS " in
+ *" $1 "*)
+ return 0
+ ;;
+ esac
+
+ return 1
+}
+
+#
+# Restore variable $1 to its pre-activation value from the activation
+# save-state, and clear the variable's bookkeeping. The caller is
+# responsible for removing the variable from SHVES_SAVED_VARS.
+#
+# The variable name is only known at run time, so the references must be
+# built with eval. For $1 = PATH, the lines below expand to:
+# [ "$SHVES_SAVEDSET_PATH" = 1 ]
+# export PATH="$SHVES_SAVED_PATH"
+#
+_shves_restore_var() {
+ if eval "[ \"\$SHVES_SAVEDSET_$1\" = 1 ]"; then
+ eval "export $1=\"\$SHVES_SAVED_$1\""
+ else
+ unset "$1"
+ fi
+
+ unset "SHVES_SAVED_$1" "SHVES_SAVEDSET_$1"
+}
+
+#
+# Begin and end word-splitting of a :-delimited value. Between the two
+# calls, an unquoted expansion splits into one word per entry, with
+# globbing disabled so entries containing wildcards pass through intact.
+#
+_shves_split_begin() {
+ case $- in
+ *f*) _shves_had_f=1 ;;
+ *) _shves_had_f=0 ;;
+ esac
+ set -f
+ _shves_old_ifs="$IFS"
+ IFS=:
+}
+
+_shves_split_end() {
+ IFS="$_shves_old_ifs"
+ if [ "$_shves_had_f" -eq 0 ]; then
+ set +f
+ fi
+ unset _shves_had_f _shves_old_ifs
+}
+
+#
+# Export variable $1 with value $2 into the live shell, recording the prior
+# value in the activation save-state (if it is not already recorded) so that
+# ves_deactivate restores it properly. Only call while an environment is
+# active.
+#
+# The save-state variables are exported so that the state survives across
+# subshell invocations. This allows non-POSIX shells (such as fish) to drive
+# sh-ves through a wrapper that runs each command in a fresh sh subshell.
+#
+_shves_live_export() {
+ if ! _shves_is_saved "$1"; then
+ # The eval lines build references to the variable named in $1.
+ # For $1 = PATH, they expand to:
+ # [ -n "${PATH+x}" ] (is PATH set at all?)
+ # SHVES_SAVED_PATH=$PATH
+ if eval "[ -n \"\${$1+x}\" ]"; then
+ eval "SHVES_SAVEDSET_$1=1"
+ eval "SHVES_SAVED_$1=\$$1"
+ export "SHVES_SAVED_$1"
+ else
+ eval "SHVES_SAVEDSET_$1=0"
+ fi
+
+ export "SHVES_SAVEDSET_$1"
+ SHVES_SAVED_VARS="$SHVES_SAVED_VARS $1"
+ export SHVES_SAVED_VARS
+ fi
+
+ export "$1=$2"
+}
+
. "$SHVES_BIN"/ves.sh
. "$SHVES_BIN"/ves-export.sh
. "$SHVES_BIN"/ves-activate.sh
@@ -61,5 +266,10 @@ _shves_check_env_exists() {
. "$SHVES_BIN"/ves-var-rm.sh
. "$SHVES_BIN"/ves-create.sh
. "$SHVES_BIN"/ves-delete.sh
+. "$SHVES_BIN"/ves-list.sh
+. "$SHVES_BIN"/ves-unset.sh
+. "$SHVES_BIN"/ves-show.sh
+. "$SHVES_BIN"/ves-copy.sh
+. "$SHVES_BIN"/ves-run.sh
-
+SH_VES_INIT=1