#! /bin/sh # # Initialize the sh-ves system within a shell session by sourcing the relevant # files to add function definitions for functionality that cannot be # implemented via scripts, as well as initialing some local variables used by # the system. This file should be sourced in your shell's rc or profile file. # # 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. 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 # prior to sourcing this file, in which case they will not be modified, # or by overwriting their values after sourcing the file. if [ -z "${XDG_DATA_HOME+x}" ]; then SHVES_DATA_DIR="$HOME/.local/share/ves" else 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 if [ -z "${SHVES_SCRIPTS_DIR+x}" ]; then SHVES_BIN="$HOME/.local/bin/ves_scripts" 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() { 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" >&2 fi return 1 fi 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 . "$SHVES_BIN"/ves-deactivate.sh . "$SHVES_BIN"/ves-var-add.sh . "$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